﻿// <copyright file="Program.cs" company="Pelican Design &amp; Development">
//      Copyright (c) Pelican Design &amp; Development. All Rights Reserved.
// </copyright>
// <author id="5c2316d3-622a-4a8d-816d-5054a48f415f">Arseni Mourzenko</author>

namespace PelicanDD.Samples.CacheInSandbox
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Reflection;
    using System.Security;
    using System.Security.Permissions;
    using System.Security.Policy;
    using System.Web;

    /// <summary>
    /// Represents the main class of the application.
    /// </summary>
    public class Program : MarshalByRefObject
    {
        /// <summary>
        /// The entry point of the application.
        /// </summary>
        [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "This part of the application is not intended to be localized.")]
        public static void Main()
        {
            const string AssemblyName = "SamplePlugIn";
            const string LibraryShortName = AssemblyName + ".dll";
            const string TypeName = "PelicanDD.Samples.CacheInSandbox.DemoPlugIn";

            string contextDirectoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string libraryFilePath = Path.Combine(contextDirectoryPath, LibraryShortName);

            var sandbox = CreateSandboxDomain(libraryFilePath);
            var handle = Activator.CreateInstanceFrom(sandbox, typeof(Program).Assembly.ManifestModule.FullyQualifiedName, typeof(Program).FullName);
            var newDomainInstance = (Program)handle.Unwrap();

            newDomainInstance.ExecuteUntrustedCode(AssemblyName, TypeName);

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey(true);
        }

        /// <summary>
        /// Executes the code of the plug-in in a sandbox.
        /// </summary>
        /// <param name="assemblyName">The name of the assembly, i.e. the name specified in <c>AssemblyTitle</c> in AssemblyInfo.cs of the plug-in.</param>
        /// <param name="typeName">The full name of the type to load. This name includes the namespace.</param>
        [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "This part of the application is not intended to be localized.")]
        [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This method is used through Reflection.")]
        public void ExecuteUntrustedCode(string assemblyName, string typeName)
        {
            var plugIn = (IPlugIn)Activator.CreateInstance(Assembly.Load(assemblyName).GetType(typeName), null);
            Console.WriteLine("Plug-in {0} is loaded. Testing permissions...", plugIn.Name);

            try
            {
                plugIn.WriteSampleFile(@"Z:\Hello.txt");
                Debug.Fail("The exception should have been thrown. The fact that this line is reached indicates that the sandbox code is running in full trust mode or that the permissions were not configured correctly.");
            }
            catch (SecurityException)
            {
                Console.WriteLine("The code failed as expected: it indicates that it is running in partial trust.");
            }

            Console.WriteLine("Use the cache...");
            plugIn.Cache = new HttpCacheWrapper(HttpRuntime.Cache);
            string valueFromCache = plugIn.UseCache("Hello World!");

            Console.WriteLine("The value from cache is: {0}", valueFromCache);
        }

        /// <summary>
        /// Creates an application domain with the restricted set of permissions.
        /// </summary>
        /// <param name="libraryPath">The full path to the plug-in library.</param>
        /// <returns>An application domain with a restricted set of permissions.</returns>
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "The code is reviewed.")]
        private static AppDomain CreateSandboxDomain(string libraryPath)
        {
            var permissions = new PermissionSet(PermissionState.None);

            // Set execution permission to be able to run code, access the plug-in and discover the .NET Framework directory.
            permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
            permissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, libraryPath));
            permissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery, Path.GetDirectoryName(typeof(object).Assembly.Location)));

            var appDomainSetup = new AppDomainSetup
            {
                ApplicationBase = Path.GetFullPath(libraryPath)
            };

            var fullTrustAssemblies = new[]
            {
                typeof(Program).Assembly.Evidence.GetHostEvidence<StrongName>(),
            };

            AppDomain sandboxAppDomain = AppDomain.CreateDomain("SampleSandbox", null, appDomainSetup, permissions, fullTrustAssemblies);
            return sandboxAppDomain;
        }
    }
}
